#include "cc_string.h"
#include "cc_common.h"
#include "cc_malloc.h"
#include <malloc.h>

char *keywordsArr[] = 
{
	"auto", "double", "int", "struct",
	"break", "else", "long", "switch",
	"case", "enum", "register", "typedef",
	"char", "extern", "return", "union",
	"const", "float", "short", "unsigned",
	"continue", "for", "signed", "void",
	"default", "goto", "sizeof", "volatile",
	"do", "if", "static", "while",
	"restrict"
};

int keywordsCnt = sizeof(keywordsArr) / sizeof(keywordsArr[0]);

char * __cdecl cc_strcat(
	char *dest,
	const char *src
	)
{
	char *cp = dest;
	while(*cp++);

	--cp;
	while(*cp++ = *src++)
		;

	return dest;
}

char * __cdecl cc_strcpy(
	char *dest,
	const char *src
	)
{
	char *cp = dest;
	while(*cp++ = *src++)
		;

	return dest;
}

char * __cdecl cc_strchr(
	const char *str,
	int ch
	)
{
	while(*str && *str != ch)
		++str;
	return (*(char *)str ? (char *)str : NULL);
}

int __cdecl cc_strcmp(
	const char *str1,
	const char *str2
	)
{
	while(*str1 && *str2 && *str1 == *str2)
	{
		++str1;
		++str2;
	}
	return ((*str1 > *str2) ? 1 : ((*str1 == *str2) ? 0 : -1));
}

int __cdecl cc_stricmp(
	const char *str1,
	const char *str2
	)
{
	while(*str1 && *str2 && (*str1 == *str2 || cc_canIgnoreUpperLowerCase(*str1, *str2)))
	{
		++str1;
		++str2;
	}
	return ((*str1 > *str2) ? 1 : ((*str1 == *str2) ? 0 : -1));
}

size_t __cdecl cc_strlen(
	const char *str
	)
{
	const char *temp = str;
	while(*temp++)
		;
	return (temp - str - 1);
}

size_t __cdecl cc_strnlen(
	const char *str,
	size_t maxSize
	)
{
	const char *temp = str;
	size_t maxSizeTemp = maxSize;
	while(maxSize-- && *temp++)
		;
	return ((maxSize == (size_t)-1) ? maxSizeTemp : (temp - str - 1));
}

char * __cdecl cc_strncat(
	char *dest,
	const char *src,
	size_t cnt
	)
{
	char *destCp = dest;
	while(*destCp++)
		;
	--destCp;
	while(cnt-- && (*destCp++ = *src++))
		;

	*destCp = '\0';
	return dest;
}

int __cdecl cc_strncmp(
	const char *str1,
	const char *str2,
	size_t cnt
	)
{
	while(cnt-- && *str1 && *str2 && (*str1 == *str2))
	{
		++str1;
		++str2;
	}

	if(cnt == (size_t)-1)
	{
		return 0;
	}

	return ((*str1 > *str2) ? 1 : ((*str1 == *str2) ? 0 : -1));
}

size_t __cdecl cc_strncnt(
	const char *str,
	size_t cnt
	)
{
	const char *cp = str;
	size_t cntCp = cnt;

	while(cnt-- && *cp++)
		;
	return (cnt == (size_t)-1 ? cntCp : (cp - str - 1));
}

char * __cdecl cc_strncpy(
	char *dest,
	const char *src,
	size_t cnt
	)
{
	char *destCp = dest;
	while(cnt-- && (*dest++ = *src++))
		;

	while(cnt-- != (size_t)-1)	// padding with zeros
		*dest++ = '\0';	

	return destCp;
}

char * __cdecl cc_strstr(
	const char *str1,
	const char *str2
	)
{
	char *str1Cp = (char *)str1;
	char *s1, *s2;

	if(!*str2)
		return (char *)str1;

	while(*str1Cp)
	{
		s1 = str1Cp;
		s2 = (char *)str2;
		while(*s1 && *s2 && !(*s1 - *s2))
			++s1, ++s2;

		if(!*s2)
			return str1Cp;

		++str1Cp;
	}
	return NULL;
}

char * __cdecl cc_strtok(
	char *str,
	const char *delim
	)
{
	static char *last;
	char *strCp = str;		// backup the str head pointer
	int hasFoundToken = 0;
	if(!str)
	{
		strCp = str = last;		// if str is NULL, update the str head pointer
	}

	while(*str)
	{
		if(cc_strchr(delim, *str))	// if found the delims in the str
		{
				++str;
				if(!hasFoundToken)
				{
					++strCp;
				}
				else	// if hasFoundToken
				{
					if(strCp != str)		
							break;
				}

				continue;
		}

		hasFoundToken = 1;
		++str;
	}
	
	if(!*str)
		return NULL;

	*(str - 1) = '\0';	// end the token by null character
	last = str;		// save the last pointer
	return strCp;
}

char * __cdecl cc_strdup(
	const char *str
	)
{
	void *strDup = cc_malloc(cc_strlen(str) + 1);
	if(!strDup)
		return NULL;

	cc_strcpy(strDup, str);
	return (char *)strDup;
}

void * __cdecl cc_skipBlankCharacters(
	INOUT const void **strHead
	)
{
	while(cc_isspace(**(char **)strHead))
		(*(char *)strHead)++;

	return *strHead;
}

int __cdecl cc_isIdentifier(
	const char *str 
	)
{
	return (cc_isIdentifierFirstChar(*str) && cc_isIdentifierAfterFirstChar(str + 1)); 
}

int __cdecl cc_isIdentifierFirstChar( 
	int ch 
	)
{
	return (cc_isalpha(ch) || ch == '_');
}

int __cdecl cc_isIdentifierAfterFirstChar( 
	const char *str 
	)
{
	if(!str)
		return 0;

	while(*str)
	{
		if(!(cc_isIdentifierFirstChar(*str) || cc_isdigit(*str)))
			return 0;

		++str;
	}

	return 1;
}

int __cdecl cc_canIgnoreUpperLowerCase( 
	const int ch1,
	const int ch2 
	)
{
	return (cc_isalpha(ch1) && cc_isalpha(ch2) && cc_abs(ch1 - ch2) == 32);
}

char * __cdecl cc_strnset( 
	char *str, 
	int val, 
	size_t cnt 
	)
{
	char *strCp = str;
	while(cnt-- && *str)
		*str++ = val;

	return strCp;
}

char * __cdecl cc_strrev( 
	char *str 
	)
{
	char *strCp = str;
	char *strEnd = str + cc_strlen(str) - 1;

	if(strEnd < str)
		return str;

	while(str < strEnd)
	{
		SWAP(*str, *strEnd);
		++str;
		--strEnd;
	}

	return strCp;
}

int __cdecl cc_isKeyword( 
	const char *str 
	)
{
	int i = 0;
	for(; i < keywordsCnt; ++i)
	{
		if(!cc_strcmp(str, keywordsArr[i]))
			return 1;
	}
	return 0;
}

int __cdecl cc_isBinaryNum( 
	const char *str 
	)
{
	if(!*str)
		return 0;

	while(*str)
	{
		if(*str != '0' && *str != '1')
			return 0;
		++str;
	}

	return 1;
}

int __cdecl cc_isOctNum( 
	const char *str 
	)
{
	if(!*str)
		return 0;

	while(*str)
	{
		if(*str < '0' || *str > '7')
			return 0;
		++str;
	}

	return 1;
}

int __cdecl cc_isDecNum( 
	const char *str 
	)
{
	if(!*str)
		return 0;

	if(!(*str == '-' || *str == '+' || (*str >= '0' && *str <= '9')))	// maybe include flag
		return 0;

	++str;
	while(*str)
	{
		if(*str < '0' || *str > '9')
			return 0;
		++str;
	}

	return 1;
}

int __cdecl cc_isHexNum( 
	const char *str 
	)
{
	if(!*str)
		return 0;

	if(*str == '0')
	{
		++str;
		if(!*str)
			return 1;
		if(*str == 'x' || *str == 'X')
		{
			++str;
			if(*str == '\0')
				return 0;
		}
	}

	while(*str)
	{
		if(!cc_isxdigit(*str))
			return 0;
		++str;
	}

	return 1;
}

char * __cdecl cc_trimBegin( 
	char *str 
	)
{
	cc_skipBlankCharacters(&str);
	return str;
}

char * __cdecl cc_trimEnd( 
	char *str 
	)
{
	int len = cc_strlen(str);
	char *temp = str + len - 1;
	while(cc_isspace(*temp))
		--temp;
	*(temp + 1) = '\0';

	return str;
}

char * __cdecl cc_trim( 
	char *str 
	)
{
	int len = cc_strlen(str);
	char *temp = str + len - 1;

	cc_skipBlankCharacters(&str);
	while(cc_isspace(*temp))
		--temp;
	*(temp + 1) = '\0';

	return str;
}

int __cdecl cc_strBeginsWith( 
	const char *str, 
	const char *anotherStr 
	)
{
	int strLen = cc_strlen(str);
	int anotherStrLen = cc_strlen(anotherStr);

	if(strLen < anotherStrLen)
		return 0;

	if(!cc_strncmp(str, anotherStr, anotherStrLen))
		return 1;
	else
		return 0;
}

int __cdecl cc_strEndsWith( 
	const char *str, 
	const char *anotherStr 
	)
{
	int strLen = cc_strlen(str);
	int anotherStrLen = cc_strlen(anotherStr);
	if(strLen < anotherStrLen)
		return 0;

	if(!cc_strncmp(str + strLen - anotherStrLen, anotherStr, cc_strlen(anotherStr)))
		return 1;
	else
		return 0;
}

int __cdecl cc_strIndexOf( 
	const char *str, 
	char ch 
	)
{
	char *strTemp = (char *)str;
	while(*str && *str != ch)
		++str;
	return (*str) ? (str - strTemp) : -1;
}

int __cdecl cc_strLastIndexOf( 
	const char *str, 
	char ch 
	)
{
	char *strTemp = (char *)str + cc_strlen(str) - 1;
	while(*strTemp && *strTemp != ch)
		--strTemp;
	return (*str) ? (strTemp - str) : -1;
}

char * __cdecl cc_strIndexOfStr( 
	const char *str, 
	const char *patternStr 
	)
{
	int strLen = cc_strlen(str);
	int patternStrLen = cc_strlen(patternStr);
	int index = 0;
	if(strLen < patternStrLen)
		return 0;

	for(; index < strLen; ++index)
	{
		if(!cc_strncmp(str + index, patternStr, patternStrLen))
			return (str + index);
	}
	return NULL;
}

int __cdecl cc_getNumLenByStr( 
	unsigned n
	)
{
	int len = 1;
	n /= 10;
	while(n)
	{
		n /= 10;
		++len;
	}
	return len;
}

void __cdecl cc_toUpperStr( 
	char *str 
	)
{
	while(*str)
	{
		*str = cc_toupper(*str);
		++str;
	}
}

void __cdecl cc_toLowerStr(
	char *str 
	)
{
	while(*str)
	{
		*str = cc_tolower(*str);
		++str;
	}
}

char * __cdecl cc_delChar( 
	char *str, 
	char ch 
	)
{
	char *firstFillPointer;
	char *copyPointer;
	char *retStr;
	while(*str == ch)
		++str;

	retStr = str;		// get the first char that isn't ch.
	++str;
	while(*str)
	{
		if(*str != ch)
		{
			++str;
			continue;
		}
		else
		{
			firstFillPointer = str;	// get the first char that should be deleted.
			copyPointer = firstFillPointer + 1;
			break;
		}
	}

	while(*copyPointer)
	{
		if(*copyPointer != ch)
		{
			*firstFillPointer = *copyPointer;
			++firstFillPointer;
		}
		++copyPointer;
	}
	*firstFillPointer = '\0';		// the end of the new str

	return retStr;
}
